/*
 * synergy -- mouse and keyboard sharing utility
 * Copyright (C) 2012-2016 Symless Ltd.
 * Copyright (C) 2004 Chris Schoeneman
 * 
 * This package is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * found in the file LICENSE that should have accompanied this file.
 * 
 * This package is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "synergy/KeyState.h"
#include "base/Log.h"

#include <cstring>
#include <algorithm>
#include <iterator>
#include <list>

static const KeyButton kButtonMask = (KeyButton)(IKeyState::kNumButtons - 1);

static const KeyID s_decomposeTable[] = {
	// spacing version of dead keys
	0x0060, 0x0300, 0x0020, 0, // grave,        dead_grave,       space
	0x00b4, 0x0301, 0x0020, 0, // acute,        dead_acute,       space
	0x005e, 0x0302, 0x0020, 0, // asciicircum,  dead_circumflex,  space
	0x007e, 0x0303, 0x0020, 0, // asciitilde,   dead_tilde,       space
	0x00a8, 0x0308, 0x0020, 0, // diaeresis,    dead_diaeresis,   space
	0x00b0, 0x030a, 0x0020, 0, // degree,       dead_abovering,   space
	0x00b8, 0x0327, 0x0020, 0, // cedilla,      dead_cedilla,     space
	0x02db, 0x0328, 0x0020, 0, // ogonek,       dead_ogonek,      space
	0x02c7, 0x030c, 0x0020, 0, // caron,        dead_caron,       space
	0x02d9, 0x0307, 0x0020, 0, // abovedot,     dead_abovedot,    space
	0x02dd, 0x030b, 0x0020, 0, // doubleacute,  dead_doubleacute, space
	0x02d8, 0x0306, 0x0020, 0, // breve,        dead_breve,       space
	0x00af, 0x0304, 0x0020, 0, // macron,       dead_macron,      space

	// Latin-1 (ISO 8859-1)
	0x00c0, 0x0300, 0x0041, 0, // Agrave,       dead_grave,       A
	0x00c1, 0x0301, 0x0041, 0, // Aacute,       dead_acute,       A
	0x00c2, 0x0302, 0x0041, 0, // Acircumflex,  dead_circumflex,  A
	0x00c3, 0x0303, 0x0041, 0, // Atilde,       dead_tilde,       A
	0x00c4, 0x0308, 0x0041, 0, // Adiaeresis,   dead_diaeresis,   A
	0x00c5, 0x030a, 0x0041, 0, // Aring,        dead_abovering,   A
	0x00c7, 0x0327, 0x0043, 0, // Ccedilla,     dead_cedilla,     C
	0x00c8, 0x0300, 0x0045, 0, // Egrave,       dead_grave,       E
	0x00c9, 0x0301, 0x0045, 0, // Eacute,       dead_acute,       E
	0x00ca, 0x0302, 0x0045, 0, // Ecircumflex,  dead_circumflex,  E
	0x00cb, 0x0308, 0x0045, 0, // Ediaeresis,   dead_diaeresis,   E
	0x00cc, 0x0300, 0x0049, 0, // Igrave,       dead_grave,       I
	0x00cd, 0x0301, 0x0049, 0, // Iacute,       dead_acute,       I
	0x00ce, 0x0302, 0x0049, 0, // Icircumflex,  dead_circumflex,  I
	0x00cf, 0x0308, 0x0049, 0, // Idiaeresis,   dead_diaeresis,   I
	0x00d1, 0x0303, 0x004e, 0, // Ntilde,       dead_tilde,       N
	0x00d2, 0x0300, 0x004f, 0, // Ograve,       dead_grave,       O
	0x00d3, 0x0301, 0x004f, 0, // Oacute,       dead_acute,       O
	0x00d4, 0x0302, 0x004f, 0, // Ocircumflex,  dead_circumflex,  O
	0x00d5, 0x0303, 0x004f, 0, // Otilde,       dead_tilde,       O
	0x00d6, 0x0308, 0x004f, 0, // Odiaeresis,   dead_diaeresis,   O
	0x00d9, 0x0300, 0x0055, 0, // Ugrave,       dead_grave,       U
	0x00da, 0x0301, 0x0055, 0, // Uacute,       dead_acute,       U
	0x00db, 0x0302, 0x0055, 0, // Ucircumflex,  dead_circumflex,  U
	0x00dc, 0x0308, 0x0055, 0, // Udiaeresis,   dead_diaeresis,   U
	0x00dd, 0x0301, 0x0059, 0, // Yacute,       dead_acute,       Y
	0x00e0, 0x0300, 0x0061, 0, // agrave,       dead_grave,       a
	0x00e1, 0x0301, 0x0061, 0, // aacute,       dead_acute,       a
	0x00e2, 0x0302, 0x0061, 0, // acircumflex,  dead_circumflex,  a
	0x00e3, 0x0303, 0x0061, 0, // atilde,       dead_tilde,       a
	0x00e4, 0x0308, 0x0061, 0, // adiaeresis,   dead_diaeresis,   a
	0x00e5, 0x030a, 0x0061, 0, // aring,        dead_abovering,   a
	0x00e7, 0x0327, 0x0063, 0, // ccedilla,     dead_cedilla,     c
	0x00e8, 0x0300, 0x0065, 0, // egrave,       dead_grave,       e
	0x00e9, 0x0301, 0x0065, 0, // eacute,       dead_acute,       e
	0x00ea, 0x0302, 0x0065, 0, // ecircumflex,  dead_circumflex,  e
	0x00eb, 0x0308, 0x0065, 0, // ediaeresis,   dead_diaeresis,   e
	0x00ec, 0x0300, 0x0069, 0, // igrave,       dead_grave,       i
	0x00ed, 0x0301, 0x0069, 0, // iacute,       dead_acute,       i
	0x00ee, 0x0302, 0x0069, 0, // icircumflex,  dead_circumflex,  i
	0x00ef, 0x0308, 0x0069, 0, // idiaeresis,   dead_diaeresis,   i
	0x00f1, 0x0303, 0x006e, 0, // ntilde,       dead_tilde,       n
	0x00f2, 0x0300, 0x006f, 0, // ograve,       dead_grave,       o
	0x00f3, 0x0301, 0x006f, 0, // oacute,       dead_acute,       o
	0x00f4, 0x0302, 0x006f, 0, // ocircumflex,  dead_circumflex,  o
	0x00f5, 0x0303, 0x006f, 0, // otilde,       dead_tilde,       o
	0x00f6, 0x0308, 0x006f, 0, // odiaeresis,   dead_diaeresis,   o
	0x00f9, 0x0300, 0x0075, 0, // ugrave,       dead_grave,       u
	0x00fa, 0x0301, 0x0075, 0, // uacute,       dead_acute,       u
	0x00fb, 0x0302, 0x0075, 0, // ucircumflex,  dead_circumflex,  u
	0x00fc, 0x0308, 0x0075, 0, // udiaeresis,   dead_diaeresis,   u
	0x00fd, 0x0301, 0x0079, 0, // yacute,       dead_acute,       y
	0x00ff, 0x0308, 0x0079, 0, // ydiaeresis,   dead_diaeresis,   y

	// Latin-2 (ISO 8859-2)
	0x0104, 0x0328, 0x0041, 0, // Aogonek,      dead_ogonek,      A
	0x013d, 0x030c, 0x004c, 0, // Lcaron,       dead_caron,       L
	0x015a, 0x0301, 0x0053, 0, // Sacute,       dead_acute,       S
	0x0160, 0x030c, 0x0053, 0, // Scaron,       dead_caron,       S
	0x015e, 0x0327, 0x0053, 0, // Scedilla,     dead_cedilla,     S
	0x0164, 0x030c, 0x0054, 0, // Tcaron,       dead_caron,       T
	0x0179, 0x0301, 0x005a, 0, // Zacute,       dead_acute,       Z
	0x017d, 0x030c, 0x005a, 0, // Zcaron,       dead_caron,       Z
	0x017b, 0x0307, 0x005a, 0, // Zabovedot,    dead_abovedot,    Z
	0x0105, 0x0328, 0x0061, 0, // aogonek,      dead_ogonek,      a
	0x013e, 0x030c, 0x006c, 0, // lcaron,       dead_caron,       l
	0x015b, 0x0301, 0x0073, 0, // sacute,       dead_acute,       s
	0x0161, 0x030c, 0x0073, 0, // scaron,       dead_caron,       s
	0x015f, 0x0327, 0x0073, 0, // scedilla,     dead_cedilla,     s
	0x0165, 0x030c, 0x0074, 0, // tcaron,       dead_caron,       t
	0x017a, 0x0301, 0x007a, 0, // zacute,       dead_acute,       z
	0x017e, 0x030c, 0x007a, 0, // zcaron,       dead_caron,       z
	0x017c, 0x0307, 0x007a, 0, // zabovedot,    dead_abovedot,    z
	0x0154, 0x0301, 0x0052, 0, // Racute,       dead_acute,       R
	0x0102, 0x0306, 0x0041, 0, // Abreve,       dead_breve,       A
	0x0139, 0x0301, 0x004c, 0, // Lacute,       dead_acute,       L
	0x0106, 0x0301, 0x0043, 0, // Cacute,       dead_acute,       C
	0x010c, 0x030c, 0x0043, 0, // Ccaron,       dead_caron,       C
	0x0118, 0x0328, 0x0045, 0, // Eogonek,      dead_ogonek,      E
	0x011a, 0x030c, 0x0045, 0, // Ecaron,       dead_caron,       E
	0x010e, 0x030c, 0x0044, 0, // Dcaron,       dead_caron,       D
	0x0143, 0x0301, 0x004e, 0, // Nacute,       dead_acute,       N
	0x0147, 0x030c, 0x004e, 0, // Ncaron,       dead_caron,       N
	0x0150, 0x030b, 0x004f, 0, // Odoubleacute, dead_doubleacute, O
	0x0158, 0x030c, 0x0052, 0, // Rcaron,       dead_caron,       R
	0x016e, 0x030a, 0x0055, 0, // Uring,        dead_abovering,   U
	0x0170, 0x030b, 0x0055, 0, // Udoubleacute, dead_doubleacute, U
	0x0162, 0x0327, 0x0054, 0, // Tcedilla,     dead_cedilla,     T
	0x0155, 0x0301, 0x0072, 0, // racute,       dead_acute,       r
	0x0103, 0x0306, 0x0061, 0, // abreve,       dead_breve,       a
	0x013a, 0x0301, 0x006c, 0, // lacute,       dead_acute,       l
	0x0107, 0x0301, 0x0063, 0, // cacute,       dead_acute,       c
	0x010d, 0x030c, 0x0063, 0, // ccaron,       dead_caron,       c
	0x0119, 0x0328, 0x0065, 0, // eogonek,      dead_ogonek,      e
	0x011b, 0x030c, 0x0065, 0, // ecaron,       dead_caron,       e
	0x010f, 0x030c, 0x0064, 0, // dcaron,       dead_caron,       d
	0x0144, 0x0301, 0x006e, 0, // nacute,       dead_acute,       n
	0x0148, 0x030c, 0x006e, 0, // ncaron,       dead_caron,       n
	0x0151, 0x030b, 0x006f, 0, // odoubleacute, dead_doubleacute, o
	0x0159, 0x030c, 0x0072, 0, // rcaron,       dead_caron,       r
	0x016f, 0x030a, 0x0075, 0, // uring,        dead_abovering,   u
	0x0171, 0x030b, 0x0075, 0, // udoubleacute, dead_doubleacute, u
	0x0163, 0x0327, 0x0074, 0, // tcedilla,     dead_cedilla,     t

	// Latin-3 (ISO 8859-3)
	0x0124, 0x0302, 0x0048, 0, // Hcircumflex,  dead_circumflex,  H
	0x0130, 0x0307, 0x0049, 0, // Iabovedot,	dead_abovedot,    I
	0x011e, 0x0306, 0x0047, 0, // Gbreve,		dead_breve, 	  G
	0x0134, 0x0302, 0x004a, 0, // Jcircumflex,  dead_circumflex,  J
	0x0125, 0x0302, 0x0068, 0, // hcircumflex,  dead_circumflex,  h
	0x011f, 0x0306, 0x0067, 0, // gbreve,		dead_breve, 	  g
	0x0135, 0x0302, 0x006a, 0, // jcircumflex,  dead_circumflex,  j
	0x010a, 0x0307, 0x0043, 0, // Cabovedot,	dead_abovedot,    C
	0x0108, 0x0302, 0x0043, 0, // Ccircumflex,  dead_circumflex,  C
	0x0120, 0x0307, 0x0047, 0, // Gabovedot,	dead_abovedot,    G
	0x011c, 0x0302, 0x0047, 0, // Gcircumflex,  dead_circumflex,  G
	0x016c, 0x0306, 0x0055, 0, // Ubreve,		dead_breve, 	  U
	0x015c, 0x0302, 0x0053, 0, // Scircumflex,  dead_circumflex,  S
	0x010b, 0x0307, 0x0063, 0, // cabovedot,	dead_abovedot,    c
	0x0109, 0x0302, 0x0063, 0, // ccircumflex,  dead_circumflex,  c
	0x0121, 0x0307, 0x0067, 0, // gabovedot,	dead_abovedot,    g
	0x011d, 0x0302, 0x0067, 0, // gcircumflex,  dead_circumflex,  g
	0x016d, 0x0306, 0x0075, 0, // ubreve,		dead_breve, 	  u
	0x015d, 0x0302, 0x0073, 0, // scircumflex,  dead_circumflex,  s

	// Latin-4 (ISO 8859-4)
	0x0156, 0x0327, 0x0052, 0, // Rcedilla, 	dead_cedilla,	  R
	0x0128, 0x0303, 0x0049, 0, // Itilde,		dead_tilde, 	  I
	0x013b, 0x0327, 0x004c, 0, // Lcedilla, 	dead_cedilla,	  L
	0x0112, 0x0304, 0x0045, 0, // Emacron,  	dead_macron,	  E
	0x0122, 0x0327, 0x0047, 0, // Gcedilla, 	dead_cedilla,	  G
	0x0157, 0x0327, 0x0072, 0, // rcedilla, 	dead_cedilla,	  r
	0x0129, 0x0303, 0x0069, 0, // itilde,		dead_tilde, 	  i
	0x013c, 0x0327, 0x006c, 0, // lcedilla, 	dead_cedilla,	  l
	0x0113, 0x0304, 0x0065, 0, // emacron,  	dead_macron,	  e
	0x0123, 0x0327, 0x0067, 0, // gcedilla, 	dead_cedilla,	  g
	0x0100, 0x0304, 0x0041, 0, // Amacron,  	dead_macron,	  A
	0x012e, 0x0328, 0x0049, 0, // Iogonek,  	dead_ogonek,	  I
	0x0116, 0x0307, 0x0045, 0, // Eabovedot,	dead_abovedot,    E
	0x012a, 0x0304, 0x0049, 0, // Imacron,  	dead_macron,	  I
	0x0145, 0x0327, 0x004e, 0, // Ncedilla, 	dead_cedilla,	  N
	0x014c, 0x0304, 0x004f, 0, // Omacron,  	dead_macron,	  O
	0x0136, 0x0327, 0x004b, 0, // Kcedilla, 	dead_cedilla,	  K
	0x0172, 0x0328, 0x0055, 0, // Uogonek,  	dead_ogonek,	  U
	0x0168, 0x0303, 0x0055, 0, // Utilde,		dead_tilde, 	  U
	0x016a, 0x0304, 0x0055, 0, // Umacron,  	dead_macron,	  U
	0x0101, 0x0304, 0x0061, 0, // amacron,  	dead_macron,	  a
	0x012f, 0x0328, 0x0069, 0, // iogonek,  	dead_ogonek,	  i
	0x0117, 0x0307, 0x0065, 0, // eabovedot,	dead_abovedot,    e
	0x012b, 0x0304, 0x0069, 0, // imacron,  	dead_macron,	  i
	0x0146, 0x0327, 0x006e, 0, // ncedilla, 	dead_cedilla,	  n
	0x014d, 0x0304, 0x006f, 0, // omacron,  	dead_macron,	  o
	0x0137, 0x0327, 0x006b, 0, // kcedilla, 	dead_cedilla,	  k
	0x0173, 0x0328, 0x0075, 0, // uogonek,  	dead_ogonek,	  u
	0x0169, 0x0303, 0x0075, 0, // utilde,		dead_tilde, 	  u
	0x016b, 0x0304, 0x0075, 0, // umacron,  	dead_macron,	  u

	// Latin-8 (ISO 8859-14)
	0x1e02, 0x0307, 0x0042, 0, // Babovedot,	dead_abovedot,    B
	0x1e03, 0x0307, 0x0062, 0, // babovedot,	dead_abovedot,    b
	0x1e0a, 0x0307, 0x0044, 0, // Dabovedot,	dead_abovedot,    D
	0x1e80, 0x0300, 0x0057, 0, // Wgrave,		dead_grave, 	  W
	0x1e82, 0x0301, 0x0057, 0, // Wacute,		dead_acute, 	  W
	0x1e0b, 0x0307, 0x0064, 0, // dabovedot,	dead_abovedot,    d
	0x1ef2, 0x0300, 0x0059, 0, // Ygrave,		dead_grave, 	  Y
	0x1e1e, 0x0307, 0x0046, 0, // Fabovedot,	dead_abovedot,    F
	0x1e1f, 0x0307, 0x0066, 0, // fabovedot,	dead_abovedot,    f
	0x1e40, 0x0307, 0x004d, 0, // Mabovedot,	dead_abovedot,    M
	0x1e41, 0x0307, 0x006d, 0, // mabovedot,	dead_abovedot,    m
	0x1e56, 0x0307, 0x0050, 0, // Pabovedot,	dead_abovedot,    P
	0x1e81, 0x0300, 0x0077, 0, // wgrave,		dead_grave, 	  w
	0x1e57, 0x0307, 0x0070, 0, // pabovedot,	dead_abovedot,    p
	0x1e83, 0x0301, 0x0077, 0, // wacute,		dead_acute, 	  w
	0x1e60, 0x0307, 0x0053, 0, // Sabovedot,	dead_abovedot,    S
	0x1ef3, 0x0300, 0x0079, 0, // ygrave,		dead_grave, 	  y
	0x1e84, 0x0308, 0x0057, 0, // Wdiaeresis,	dead_diaeresis,   W
	0x1e85, 0x0308, 0x0077, 0, // wdiaeresis,	dead_diaeresis,   w
	0x1e61, 0x0307, 0x0073, 0, // sabovedot,	dead_abovedot,    s
	0x0174, 0x0302, 0x0057, 0, // Wcircumflex,  dead_circumflex,  W
	0x1e6a, 0x0307, 0x0054, 0, // Tabovedot,	dead_abovedot,    T
	0x0176, 0x0302, 0x0059, 0, // Ycircumflex,  dead_circumflex,  Y
	0x0175, 0x0302, 0x0077, 0, // wcircumflex,  dead_circumflex,  w
	0x1e6b, 0x0307, 0x0074, 0, // tabovedot,	dead_abovedot,    t
	0x0177, 0x0302, 0x0079, 0, // ycircumflex,  dead_circumflex,  y

	// Latin-9 (ISO 8859-15)
	0x0178, 0x0308, 0x0059, 0, // Ydiaeresis,   dead_diaeresis,   Y

	// Compose key sequences
	0x00c6, kKeyCompose, 0x0041, 0x0045, 0, // AE,  		   A,		   E
	0x00c1, kKeyCompose, 0x0041, 0x0027, 0, // Aacute,  	   A,		   apostrophe
	0x00c2, kKeyCompose, 0x0041, 0x0053, 0, // Acircumflex,    A,		   asciicircum
	0x00c3, kKeyCompose, 0x0041, 0x0022, 0, // Adiaeresis,     A,		   quotedbl
	0x00c0, kKeyCompose, 0x0041, 0x0060, 0, // Agrave,  	   A,		   grave
	0x00c5, kKeyCompose, 0x0041, 0x002a, 0, // Aring,		   A,		   asterisk
	0x00c3, kKeyCompose, 0x0041, 0x007e, 0, // Atilde,  	   A,		   asciitilde
	0x00c7, kKeyCompose, 0x0043, 0x002c, 0, // Ccedilla,	   C,		   comma
	0x00d0, kKeyCompose, 0x0044, 0x002d, 0, // ETH, 		   D,		   minus
	0x00c9, kKeyCompose, 0x0045, 0x0027, 0, // Eacute,  	   E,		   apostrophe
	0x00ca, kKeyCompose, 0x0045, 0x0053, 0, // Ecircumflex,    E,		   asciicircum
	0x00cb, kKeyCompose, 0x0045, 0x0022, 0, // Ediaeresis,     E,		   quotedbl
	0x00c8, kKeyCompose, 0x0045, 0x0060, 0, // Egrave,  	   E,		   grave
	0x00cd, kKeyCompose, 0x0049, 0x0027, 0, // Iacute,  	   I,		   apostrophe
	0x00ce, kKeyCompose, 0x0049, 0x0053, 0, // Icircumflex,    I,		   asciicircum
	0x00cf, kKeyCompose, 0x0049, 0x0022, 0, // Idiaeresis,     I,		   quotedbl
	0x00cc, kKeyCompose, 0x0049, 0x0060, 0, // Igrave,  	   I,		   grave
	0x00d1, kKeyCompose, 0x004e, 0x007e, 0, // Ntilde,  	   N,		   asciitilde
	0x00d3, kKeyCompose, 0x004f, 0x0027, 0, // Oacute,  	   O,		   apostrophe
	0x00d4, kKeyCompose, 0x004f, 0x0053, 0, // Ocircumflex,    O,		   asciicircum
	0x00d6, kKeyCompose, 0x004f, 0x0022, 0, // Odiaeresis,     O,		   quotedbl
	0x00d2, kKeyCompose, 0x004f, 0x0060, 0, // Ograve,  	   O,		   grave
	0x00d8, kKeyCompose, 0x004f, 0x002f, 0, // Ooblique,	   O,		   slash
	0x00d5, kKeyCompose, 0x004f, 0x007e, 0, // Otilde,  	   O,		   asciitilde
	0x00de, kKeyCompose, 0x0054, 0x0048, 0, // THORN,		   T,		   H
	0x00da, kKeyCompose, 0x0055, 0x0027, 0, // Uacute,  	   U,		   apostrophe
	0x00db, kKeyCompose, 0x0055, 0x0053, 0, // Ucircumflex,    U,		   asciicircum
	0x00dc, kKeyCompose, 0x0055, 0x0022, 0, // Udiaeresis,     U,		   quotedbl
	0x00d9, kKeyCompose, 0x0055, 0x0060, 0, // Ugrave,  	   U,		   grave
	0x00dd, kKeyCompose, 0x0059, 0x0027, 0, // Yacute,  	   Y,		   apostrophe
	0x00e1, kKeyCompose, 0x0061, 0x0027, 0, // aacute,  	   a,		   apostrophe
	0x00e2, kKeyCompose, 0x0061, 0x0053, 0, // acircumflex,    a,		   asciicircum
	0x00b4, kKeyCompose, 0x0027, 0x0027, 0, // acute,		   apostrophe, apostrophe
	0x00e4, kKeyCompose, 0x0061, 0x0022, 0, // adiaeresis,     a,		   quotedbl
	0x00e6, kKeyCompose, 0x0061, 0x0065, 0, // ae,  		   a,		   e
	0x00e0, kKeyCompose, 0x0061, 0x0060, 0, // agrave,  	   a,		   grave
	0x00e5, kKeyCompose, 0x0061, 0x002a, 0, // aring,		   a,		   asterisk
	0x0040, kKeyCompose, 0x0041, 0x0054, 0, // at,  		   A,		   T
	0x00e3, kKeyCompose, 0x0061, 0x007e, 0, // atilde,  	   a,		   asciitilde
	0x005c, kKeyCompose, 0x002f, 0x002f, 0, // backslash,	   slash,	   slash
	0x007c, kKeyCompose, 0x004c, 0x0056, 0, // bar, 		   L,		   V
	0x007b, kKeyCompose, 0x0028, 0x002d, 0, // braceleft,	   parenleft,  minus
	0x007d, kKeyCompose, 0x0029, 0x002d, 0, // braceright,     parenright, minus
	0x005b, kKeyCompose, 0x0028, 0x0028, 0, // bracketleft,    parenleft,  parenleft
	0x005d, kKeyCompose, 0x0029, 0x0029, 0, // bracketright,   parenright, parenright
	0x00a6, kKeyCompose, 0x0042, 0x0056, 0, // brokenbar,	   B,		   V
	0x00e7, kKeyCompose, 0x0063, 0x002c, 0, // ccedilla,	   c,		   comma
	0x00b8, kKeyCompose, 0x002c, 0x002c, 0, // cedilla, 	   comma,	   comma
	0x00a2, kKeyCompose, 0x0063, 0x002f, 0, // cent,		   c,		   slash
	0x00a9, kKeyCompose, 0x0028, 0x0063, 0, // copyright,	   parenleft,  c
	0x00a4, kKeyCompose, 0x006f, 0x0078, 0, // currency,	   o,		   x
	0x00b0, kKeyCompose, 0x0030, 0x0053, 0, // degree,  	   0,		   asciicircum
	0x00a8, kKeyCompose, 0x0022, 0x0022, 0, // diaeresis,	   quotedbl,   quotedbl
	0x00f7, kKeyCompose, 0x003a, 0x002d, 0, // division,	   colon,	   minus
	0x00e9, kKeyCompose, 0x0065, 0x0027, 0, // eacute,  	   e,		   apostrophe
	0x00ea, kKeyCompose, 0x0065, 0x0053, 0, // ecircumflex,    e,		   asciicircum
	0x00eb, kKeyCompose, 0x0065, 0x0022, 0, // ediaeresis,     e,		   quotedbl
	0x00e8, kKeyCompose, 0x0065, 0x0060, 0, // egrave,  	   e,		   grave
	0x00f0, kKeyCompose, 0x0064, 0x002d, 0, // eth, 		   d,		   minus
	0x00a1, kKeyCompose, 0x0021, 0x0021, 0, // exclamdown,     exclam,     exclam
	0x00ab, kKeyCompose, 0x003c, 0x003c, 0, // guillemotleft,  less,	   less
	0x00bb, kKeyCompose, 0x003e, 0x003e, 0, // guillemotright, greater,    greater
	0x0023, kKeyCompose, 0x002b, 0x002b, 0, // numbersign,     plus,	   plus
	0x00ad, kKeyCompose, 0x002d, 0x002d, 0, // hyphen,  	   minus,	   minus
	0x00ed, kKeyCompose, 0x0069, 0x0027, 0, // iacute,  	   i,		   apostrophe
	0x00ee, kKeyCompose, 0x0069, 0x0053, 0, // icircumflex,    i,		   asciicircum
	0x00ef, kKeyCompose, 0x0069, 0x0022, 0, // idiaeresis,     i,		   quotedbl
	0x00ec, kKeyCompose, 0x0069, 0x0060, 0, // igrave,  	   i,		   grave
	0x00af, kKeyCompose, 0x002d, 0x0053, 0, // macron,  	   minus,	   asciicircum
	0x00ba, kKeyCompose, 0x006f, 0x005f, 0, // masculine,	   o,		   underscore
	0x00b5, kKeyCompose, 0x0075, 0x002f, 0, // mu,  		   u,		   slash
	0x00d7, kKeyCompose, 0x0078, 0x0078, 0, // multiply,	   x,		   x
	0x00a0, kKeyCompose, 0x0020, 0x0020, 0, // nobreakspace,   space,	   space
	0x00ac, kKeyCompose, 0x002c, 0x002d, 0, // notsign, 	   comma,	   minus
	0x00f1, kKeyCompose, 0x006e, 0x007e, 0, // ntilde,  	   n,		   asciitilde
	0x00f3, kKeyCompose, 0x006f, 0x0027, 0, // oacute,  	   o,		   apostrophe
	0x00f4, kKeyCompose, 0x006f, 0x0053, 0, // ocircumflex,    o,		   asciicircum
	0x00f6, kKeyCompose, 0x006f, 0x0022, 0, // odiaeresis,     o,		   quotedbl
	0x00f2, kKeyCompose, 0x006f, 0x0060, 0, // ograve,  	   o,		   grave
	0x00bd, kKeyCompose, 0x0031, 0x0032, 0, // onehalf, 	   1,		   2
	0x00bc, kKeyCompose, 0x0031, 0x0034, 0, // onequarter,     1,		   4
	0x00b9, kKeyCompose, 0x0031, 0x0053, 0, // onesuperior,    1,		   asciicircum
	0x00aa, kKeyCompose, 0x0061, 0x005f, 0, // ordfeminine,    a,		   underscore
	0x00f8, kKeyCompose, 0x006f, 0x002f, 0, // oslash,  	   o,		   slash
	0x00f5, kKeyCompose, 0x006f, 0x007e, 0, // otilde,  	   o,		   asciitilde
	0x00b6, kKeyCompose, 0x0070, 0x0021, 0, // paragraph,	   p,		   exclam
	0x00b7, kKeyCompose, 0x002e, 0x002e, 0, // periodcentered, period,     period
	0x00b1, kKeyCompose, 0x002b, 0x002d, 0, // plusminus,	   plus,	   minus
	0x00bf, kKeyCompose, 0x003f, 0x003f, 0, // questiondown,   question,   question
	0x00ae, kKeyCompose, 0x0028, 0x0072, 0, // registered,     parenleft,  r
	0x00a7, kKeyCompose, 0x0073, 0x006f, 0, // section, 	   s,		   o
	0x00df, kKeyCompose, 0x0073, 0x0073, 0, // ssharp,  	   s,		   s
	0x00a3, kKeyCompose, 0x004c, 0x002d, 0, // sterling,	   L,		   minus
	0x00fe, kKeyCompose, 0x0074, 0x0068, 0, // thorn,		   t,		   h
	0x00be, kKeyCompose, 0x0033, 0x0034, 0, // threequarters,  3,		   4
	0x00b3, kKeyCompose, 0x0033, 0x0053, 0, // threesuperior,  3,		   asciicircum
	0x00b2, kKeyCompose, 0x0032, 0x0053, 0, // twosuperior,    2,		   asciicircum
	0x00fa, kKeyCompose, 0x0075, 0x0027, 0, // uacute,  	   u,		   apostrophe
	0x00fb, kKeyCompose, 0x0075, 0x0053, 0, // ucircumflex,    u,		   asciicircum
	0x00fc, kKeyCompose, 0x0075, 0x0022, 0, // udiaeresis,     u,		   quotedbl
	0x00f9, kKeyCompose, 0x0075, 0x0060, 0, // ugrave,  	   u,		   grave
	0x00fd, kKeyCompose, 0x0079, 0x0027, 0, // yacute,  	   y,		   apostrophe
	0x00ff, kKeyCompose, 0x0079, 0x0022, 0, // ydiaeresis,     y,		   quotedbl
	0x00a5, kKeyCompose, 0x0079, 0x003d, 0, // yen, 		   y,		   equal

	// end of table
	0
};

static const KeyID s_numpadTable[] = {
	kKeyKP_Space,		0x0020,
	kKeyKP_Tab,			kKeyTab,
	kKeyKP_Enter,		kKeyReturn,
	kKeyKP_F1,			kKeyF1,
	kKeyKP_F2,			kKeyF2,
	kKeyKP_F3,			kKeyF3,
	kKeyKP_F4,			kKeyF4,
	kKeyKP_Home,		kKeyHome,
	kKeyKP_Left,		kKeyLeft,
	kKeyKP_Up,			kKeyUp,
	kKeyKP_Right,		kKeyRight,
	kKeyKP_Down,		kKeyDown,
	kKeyKP_PageUp,		kKeyPageUp,
	kKeyKP_PageDown,	kKeyPageDown,
	kKeyKP_End,			kKeyEnd,
	kKeyKP_Begin,		kKeyBegin,
	kKeyKP_Insert,		kKeyInsert,
	kKeyKP_Delete,		kKeyDelete,
	kKeyKP_Equal,		0x003d,
	kKeyKP_Multiply,	0x002a,
	kKeyKP_Add,			0x002b,
	kKeyKP_Separator,	0x002c,
	kKeyKP_Subtract,	0x002d,
	kKeyKP_Decimal,		0x002e,
	kKeyKP_Divide,		0x002f,
	kKeyKP_0,			0x0030,
	kKeyKP_1,			0x0031,
	kKeyKP_2,			0x0032,
	kKeyKP_3,			0x0033,
	kKeyKP_4,			0x0034,
	kKeyKP_5,			0x0035,
	kKeyKP_6,			0x0036,
	kKeyKP_7,			0x0037,
	kKeyKP_8,			0x0038,
	kKeyKP_9,			0x0039
};

//
// KeyState
//

KeyState::KeyState(IEventQueue* events) :
	IKeyState(events),
	m_keyMapPtr(new synergy::KeyMap()),
	m_keyMap(*m_keyMapPtr),
	m_mask(0),
	m_events(events)
{
	init();
}

KeyState::KeyState(IEventQueue* events, synergy::KeyMap& keyMap) :
	IKeyState(events),
	m_keyMapPtr(0),
	m_keyMap(keyMap),
	m_mask(0),
	m_events(events)
{
	init();
}

KeyState::~KeyState()
{
	if (m_keyMapPtr)
		delete m_keyMapPtr;
}

void
KeyState::init()
{
	memset(&m_keys, 0, sizeof(m_keys));
	memset(&m_syntheticKeys, 0, sizeof(m_syntheticKeys));
	memset(&m_keyClientData, 0, sizeof(m_keyClientData));
	memset(&m_serverKeys, 0, sizeof(m_serverKeys));
}

void
KeyState::onKey(KeyButton button, bool down, KeyModifierMask newState)
{
	// update modifier state
	m_mask = newState;
	LOG((CLOG_DEBUG1 "new mask: 0x%04x", m_mask));

	// ignore bogus buttons
	button &= kButtonMask;
	if (button == 0) {
		return;
	}

	// update key state
	if (down) {
		m_keys[button]          = 1;
		m_syntheticKeys[button] = 1;
	}
	else {
		m_keys[button]          = 0;
		m_syntheticKeys[button] = 0;
	}
}

void
KeyState::sendKeyEvent(
				void* target, bool press, bool isAutoRepeat,
				KeyID key, KeyModifierMask mask,
				SInt32 count, KeyButton button)
{
	if (m_keyMap.isHalfDuplex(key, button)) {
		if (isAutoRepeat) {
			// ignore auto-repeat on half-duplex keys
		}
		else {
			m_events->addEvent(Event(m_events->forIKeyState().keyDown(), target,
							KeyInfo::alloc(key, mask, button, 1)));
			m_events->addEvent(Event(m_events->forIKeyState().keyUp(), target,
							KeyInfo::alloc(key, mask, button, 1)));
		}
	}
	else {
		if (isAutoRepeat) {
			m_events->addEvent(Event(m_events->forIKeyState().keyRepeat(), target,
							 KeyInfo::alloc(key, mask, button, count)));
		}
		else if (press) {
			m_events->addEvent(Event(m_events->forIKeyState().keyDown(), target,
							KeyInfo::alloc(key, mask, button, 1)));
		}
		else {
			m_events->addEvent(Event(m_events->forIKeyState().keyUp(), target,
							KeyInfo::alloc(key, mask, button, 1)));
		}
	}
}

void
KeyState::updateKeyMap()
{
	// get the current keyboard map
	synergy::KeyMap keyMap;
	getKeyMap(keyMap);
	m_keyMap.swap(keyMap);
	m_keyMap.finish();

	// add special keys
	addCombinationEntries();
	addKeypadEntries();
	addAliasEntries();
}

void
KeyState::updateKeyState()
{
	// reset our state
	memset(&m_keys, 0, sizeof(m_keys));
	memset(&m_syntheticKeys, 0, sizeof(m_syntheticKeys));
	memset(&m_keyClientData, 0, sizeof(m_keyClientData));
	memset(&m_serverKeys, 0, sizeof(m_serverKeys));
	m_activeModifiers.clear();

	// get the current keyboard state
	KeyButtonSet keysDown;
	pollPressedKeys(keysDown);
	for (KeyButtonSet::const_iterator i = keysDown.begin();
								i != keysDown.end(); ++i) {
		m_keys[*i] = 1;
	}

	// get the current modifier state
	m_mask = pollActiveModifiers();

	// set active modifiers
	AddActiveModifierContext addModifierContext(pollActiveGroup(), m_mask,
												m_activeModifiers);
	m_keyMap.foreachKey(&KeyState::addActiveModifierCB, &addModifierContext);

	LOG((CLOG_DEBUG1 "modifiers on update: 0x%04x", m_mask));
}

void
KeyState::addActiveModifierCB(KeyID, SInt32 group,
				synergy::KeyMap::KeyItem& keyItem, void* vcontext)
{
	AddActiveModifierContext* context =
		static_cast<AddActiveModifierContext*>(vcontext);
	if (group == context->m_activeGroup &&
		(keyItem.m_generates & context->m_mask) != 0) {
		context->m_activeModifiers.insert(std::make_pair(
								keyItem.m_generates, keyItem));
	}	
}

void
KeyState::setHalfDuplexMask(KeyModifierMask mask)
{
	m_keyMap.clearHalfDuplexModifiers();
	if ((mask & KeyModifierCapsLock) != 0) {
		m_keyMap.addHalfDuplexModifier(kKeyCapsLock);
	}
	if ((mask & KeyModifierNumLock) != 0) {
		m_keyMap.addHalfDuplexModifier(kKeyNumLock);
	}
	if ((mask & KeyModifierScrollLock) != 0) {
		m_keyMap.addHalfDuplexModifier(kKeyScrollLock);
	}
}

void
KeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton serverID)
{
	// if this server key is already down then this is probably a
	// mis-reported autorepeat.
	serverID &= kButtonMask;
	if (m_serverKeys[serverID] != 0) {
		fakeKeyRepeat(id, mask, 1, serverID);
		return;
	}

	// ignore certain keys
	if (isIgnoredKey(id, mask)) {
		LOG((CLOG_DEBUG1 "ignored key %04x %04x", id, mask));
		return;
	}

	// get keys for key press
	Keystrokes keys;
	ModifierToKeys oldActiveModifiers = m_activeModifiers;
	const synergy::KeyMap::KeyItem* keyItem =
		m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers,
								getActiveModifiersRValue(), mask, false);
	if (keyItem == NULL) {
		// a media key won't be mapped on mac, so we need to fake it in a
		// special way
		if (id == kKeyAudioDown || id == kKeyAudioUp ||
			id == kKeyAudioMute || id == kKeyAudioPlay ||
			id == kKeyAudioPrev || id == kKeyAudioNext ||
			id == kKeyBrightnessDown || id == kKeyBrightnessUp
			) {
			LOG((CLOG_DEBUG1 "emulating media key"));
			fakeMediaKey(id);
		}
		
		return;
	}
	
	KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask);
	updateModifierKeyState(localID, oldActiveModifiers, m_activeModifiers);
	if (localID != 0) {
		// note keys down
		++m_keys[localID];
		++m_syntheticKeys[localID];
		m_keyClientData[localID] = keyItem->m_client;
		m_serverKeys[serverID]   = localID;
	}

	// generate key events
	fakeKeys(keys, 1);
}

bool
KeyState::fakeKeyRepeat(
				KeyID id, KeyModifierMask mask,
				SInt32 count, KeyButton serverID)
{
	serverID &= kButtonMask;

	// if we haven't seen this button go down then ignore it
	KeyButton oldLocalID = m_serverKeys[serverID];
	if (oldLocalID == 0) {
		return false;
	}

	// get keys for key repeat
	Keystrokes keys;
	ModifierToKeys oldActiveModifiers = m_activeModifiers;
	const synergy::KeyMap::KeyItem* keyItem =
		m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers,
								getActiveModifiersRValue(), mask, true);
	if (keyItem == NULL) {
		return false;
	}
	KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask);
	if (localID == 0) {
		return false;
	}

	// if the KeyButton for the auto-repeat is not the same as for the
	// initial press then mark the initial key as released and the new
	// key as pressed.  this can happen when we auto-repeat after a
	// dead key.  for example, a dead accent followed by 'a' will
	// generate an 'a with accent' followed by a repeating 'a'.  the
	// KeyButtons for the two KeyIDs might be different.
	if (localID != oldLocalID) {
		// replace key up with previous KeyButton but leave key down
		// alone so it uses the new KeyButton.
		for (Keystrokes::iterator index = keys.begin();
								index != keys.end(); ++index) {
			if (index->m_type == Keystroke::kButton &&
				index->m_data.m_button.m_button == localID) {
				index->m_data.m_button.m_button = oldLocalID;
				break;
			}
		}

		// note that old key is now up
		--m_keys[oldLocalID];
		--m_syntheticKeys[oldLocalID];

		// note keys down
		updateModifierKeyState(localID, oldActiveModifiers, m_activeModifiers);
		++m_keys[localID];
		++m_syntheticKeys[localID];
		m_keyClientData[localID] = keyItem->m_client;
		m_serverKeys[serverID]   = localID;
	}

	// generate key events
	fakeKeys(keys, count);
	return true;
}

bool
KeyState::fakeKeyUp(KeyButton serverID)
{
	// if we haven't seen this button go down then ignore it
	KeyButton localID = m_serverKeys[serverID & kButtonMask];
	if (localID == 0) {
		return false;
	}

	// get the sequence of keys to simulate key release
	Keystrokes keys;
	keys.push_back(Keystroke(localID, false, false, m_keyClientData[localID]));

	// note keys down
	--m_keys[localID];
	--m_syntheticKeys[localID];
	m_serverKeys[serverID] = 0;

	// check if this is a modifier
	ModifierToKeys::iterator i = m_activeModifiers.begin();
	while (i != m_activeModifiers.end()) {
		if (i->second.m_button == localID && !i->second.m_lock) {
			// modifier is no longer down
			KeyModifierMask mask = i->first;

			ModifierToKeys::iterator tmp = i;
			++i;
			m_activeModifiers.erase(tmp);

			if (m_activeModifiers.count(mask) == 0) {
				// no key for modifier is down so deactivate modifier
				m_mask &= ~mask;
				LOG((CLOG_DEBUG1 "new state %04x", m_mask));
			}
		}
		else {
			++i;
		}
	}

	// generate key events
	fakeKeys(keys, 1);
	return true;
}

void
KeyState::fakeAllKeysUp()
{
	Keystrokes keys;
	for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) {
		if (m_syntheticKeys[i] > 0) {
			keys.push_back(Keystroke(i, false, false, m_keyClientData[i]));
			m_keys[i]          = 0;
			m_syntheticKeys[i] = 0;
		}
	}
	fakeKeys(keys, 1);
	memset(&m_serverKeys, 0, sizeof(m_serverKeys));
	m_activeModifiers.clear();
	m_mask = pollActiveModifiers();
}

bool
KeyState::fakeMediaKey(KeyID id)
{
	return false;
}

bool
KeyState::isKeyDown(KeyButton button) const
{
	return (m_keys[button & kButtonMask] > 0);
}

KeyModifierMask
KeyState::getActiveModifiers() const
{
	return m_mask;
}

KeyModifierMask&
KeyState::getActiveModifiersRValue()
{
	return m_mask;
}

SInt32
KeyState::getEffectiveGroup(SInt32 group, SInt32 offset) const
{
	return m_keyMap.getEffectiveGroup(group, offset);
}

bool
KeyState::isIgnoredKey(KeyID key, KeyModifierMask) const
{
	switch (key) {
	case kKeyCapsLock:
	case kKeyNumLock:
	case kKeyScrollLock:
		return true;

	default:
		return false;
	}
}

KeyButton
KeyState::getButton(KeyID id, SInt32 group) const
{
	const synergy::KeyMap::KeyItemList* items =
		m_keyMap.findCompatibleKey(id, group, 0, 0);
	if (items == NULL) {
		return 0;
	}
	else {
		return items->back().m_button;
	}
}

void
KeyState::addAliasEntries()
{
	for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) {
		// if we can't shift any kKeyTab key in a particular group but we can
		// shift kKeyLeftTab then add a shifted kKeyTab entry that matches a
		// shifted kKeyLeftTab entry.
		m_keyMap.addKeyAliasEntry(kKeyTab, g,
								KeyModifierShift, KeyModifierShift,
								kKeyLeftTab,
								KeyModifierShift, KeyModifierShift);

		// if we have no kKeyLeftTab but we do have a kKeyTab that can be
		// shifted then add kKeyLeftTab that matches a kKeyTab.
		m_keyMap.addKeyAliasEntry(kKeyLeftTab, g,
								KeyModifierShift, KeyModifierShift,
								kKeyTab,
								0, KeyModifierShift);

		// map non-breaking space to space
		m_keyMap.addKeyAliasEntry(0x20, g, 0, 0, 0xa0, 0, 0);
	}
}

void
KeyState::addKeypadEntries()
{
	// map every numpad key to its equivalent non-numpad key if it's not
	// on the keyboard.
	for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) {
		for (size_t i = 0; i < sizeof(s_numpadTable) /
								sizeof(s_numpadTable[0]); i += 2) {
			m_keyMap.addKeyCombinationEntry(s_numpadTable[i], g,
								s_numpadTable + i + 1, 1);
		}
	}
}

void
KeyState::addCombinationEntries()
{
	for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) {
		// add dead and compose key composition sequences
		for (const KeyID* i = s_decomposeTable; *i != 0; ++i) {
			// count the decomposed keys for this key
			UInt32 numKeys = 0;
			for (const KeyID* j = i; *++j != 0; ) {
				++numKeys;
			}

			// add an entry for this key
			m_keyMap.addKeyCombinationEntry(*i, g, i + 1, numKeys);

			// next key
			i += numKeys + 1;
		}
	}
}

void
KeyState::fakeKeys(const Keystrokes& keys, UInt32 count)
{
	// do nothing if no keys or no repeats
	if (count == 0 || keys.empty()) {
		return;
	}

	// generate key events
	LOG((CLOG_DEBUG1 "keystrokes:"));
	for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) {
		if (k->m_type == Keystroke::kButton && k->m_data.m_button.m_repeat) {
			// repeat from here up to but not including the next key
			// with m_repeat == false count times.
			Keystrokes::const_iterator start = k;
			while (count-- > 0) {
				// send repeating events
				for (k = start; k != keys.end() &&
								k->m_type == Keystroke::kButton &&
								k->m_data.m_button.m_repeat; ++k) {
					fakeKey(*k);
				}
			}

			// note -- k is now on the first non-repeat key after the
			// repeat keys, exactly where we'd like to continue from.
		}
		else {
			// send event
			fakeKey(*k);

			// next key
			++k;
		}
	}
}

void
KeyState::updateModifierKeyState(KeyButton button,
				const ModifierToKeys& oldModifiers,
				const ModifierToKeys& newModifiers)
{
	// get the pressed modifier buttons before and after
	synergy::KeyMap::ButtonToKeyMap oldKeys, newKeys;
	for (ModifierToKeys::const_iterator i = oldModifiers.begin();
								i != oldModifiers.end(); ++i) {
		oldKeys.insert(std::make_pair(i->second.m_button, &i->second));
	}
	for (ModifierToKeys::const_iterator i = newModifiers.begin();
								i != newModifiers.end(); ++i) {
		newKeys.insert(std::make_pair(i->second.m_button, &i->second));
	}

	// get the modifier buttons that were pressed or released
	synergy::KeyMap::ButtonToKeyMap pressed, released;
	std::set_difference(oldKeys.begin(), oldKeys.end(),
						newKeys.begin(), newKeys.end(),
						std::inserter(released, released.end()),
						ButtonToKeyLess());
	std::set_difference(newKeys.begin(), newKeys.end(),
						oldKeys.begin(), oldKeys.end(),
						std::inserter(pressed, pressed.end()),
						ButtonToKeyLess());

	// update state
	for (synergy::KeyMap::ButtonToKeyMap::const_iterator i = released.begin();
								i != released.end(); ++i) {
		if (i->first != button) {
			m_keys[i->first]          = 0;
			m_syntheticKeys[i->first] = 0;
		}
	}
	for (synergy::KeyMap::ButtonToKeyMap::const_iterator i = pressed.begin();
								i != pressed.end(); ++i) {
		if (i->first != button) {
			m_keys[i->first]          = 1;
			m_syntheticKeys[i->first] = 1;
			m_keyClientData[i->first] = i->second->m_client;
		}
	}
}

//
// KeyState::AddActiveModifierContext
//

KeyState::AddActiveModifierContext::AddActiveModifierContext(
				SInt32 group, KeyModifierMask mask,
				ModifierToKeys&	activeModifiers) :
	m_activeGroup(group),
	m_mask(mask),
	m_activeModifiers(activeModifiers)
{
	// do nothing
}
